// 版权所有2009 Go作者。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

// Package scanner为UTF-8编码的文本提供了一个扫描仪和标记器。
// 需要一个io。读卡器提供源代码，然后可以通过重复调用Scan函数将其标记为
// 。为了与
// 现有工具兼容，不允许使用NUL字符。如果源中的第一个字符
// 是UTF-8编码的字节顺序标记（BOM），则该字符将被丢弃。默认情况下，扫描器跳过空白和Go注释，并识别Go语言规范定义的所有
// 文本。它可能是
// 自定义为仅识别这些文字的子集，并识别
// 不同的标识符和空白字符。
package scanner

import (
	"bytes"
	"fmt"
	"io"
	"os"
	"unicode"
	"unicode/utf8"
)

// 位置是表示源位置的值。
// 如果行>0，则位置有效。
type Position struct {
	Filename string // 文件名，如果有
	Offset   int    // 字节偏移量，从0开始
	Line     int    // 行号，从1开始
	Column   int    // 列号，从1开始（每行字符数）
}

// 是有效的报告位置是否有效。
func (pos *Position) IsValid() bool { return pos.Line > 0 }

func (pos Position) String() string {
	s := pos.Filename
	if s == "" {
		s = "<input>"
	}
	if pos.IsValid() {
		s += fmt.Sprintf(":%d:%d", pos.Line, pos.Column)
	}
	return s
}

// 用于控制令牌识别的预定义模式位。例如，要配置扫描仪，使其只识别（Go）标识符、整数和跳过注释，请将扫描仪的模式字段设置为：
// 
// ScanIdentits | ScanInts | SkipComments 
// 
// 注释除外，如果SkipComments是
// set，则跳过注释，无法识别的令牌不会被忽略。相反，扫描器只返回各自的单个字符（或可能的子标记）。
// 例如，如果模式是ScanIdentits（而不是ScanStrings），字符串
// “foo”将被扫描为令牌序列“Ident”。
// 
// 使用GoTokens配置扫描仪，使其接受所有Go 
// 文字标记，包括Go标识符。评论将被跳过。
// 
const (
	ScanIdents     = 1 << -Ident
	ScanInts       = 1 << -Int
	ScanFloats     = 1 << -Float // 包含整数和十六进制浮点
	ScanChars      = 1 << -Char
	ScanStrings    = 1 << -String
	ScanRawStrings = 1 << -RawString
	ScanComments   = 1 << -Comment
	SkipComments   = 1 << -skipComment // 如果使用ScanComments设置，注释将变成空白
	GoTokens       = ScanIdents | ScanFloats | ScanChars | ScanStrings | ScanRawStrings | ScanComments | SkipComments
)

// 扫描的结果是这些标记之一或Unicode字符。
const (
	EOF = -(iota + 1)
	Ident
	Int
	Float
	Char
	String
	RawString
	Comment

	// 仅限内部使用
	skipComment
)

var tokenString = map[rune]string{
	EOF:       "EOF",
	Ident:     "Ident",
	Int:       "Int",
	Float:     "Float",
	Char:      "Char",
	String:    "String",
	RawString: "RawString",
	Comment:   "Comment",
}

// TokenString返回标记或Unicode字符的可打印字符串。
func TokenString(tok rune) string {
	if s, found := tokenString[tok]; found {
		return s
	}
	return fmt.Sprintf("%q", string(tok))
}

// GoWhitespace是扫描仪空白字段的默认值。
// 其值选择Go的空白字符。
const GoWhitespace = 1<<'\t' | 1<<'\n' | 1<<'\r' | 1<<' '

const bufLen = 1024 // 至少utf8。UTFMax 

// 扫描仪实现从io读取Unicode字符和令牌。读者
type Scanner struct {
	// 输入
	src io.Reader

	// 源缓冲区
	srcBuf [bufLen + 1]byte // /+1用于s.next的常见情况（）
	srcPos int              // 读取位置（srcBuf索引）
	srcEnd int              // 源端（srcBuf索引）

	// 源位置
	srcBufOffset int // 源中srcBuf[0]的字节偏移量
	line         int // 行计数
	column       int // 字符计数
	lastLineLen  int // 字符最后一行的长度（用于正确的列报告）
	lastCharLen  int // 字节最后一个字符的长度

	// 令牌文本缓冲区
	// 通常，令牌文本完全存储在srcBuf中，但一般情况下，令牌文本的头可以缓冲在tokBuf中，而令牌文本的尾可以存储在srcBuf中。
	tokBuf bytes.Buffer // 不再在srcBuf中的令牌文本头
	tokPos int          // 令牌文本尾位置（srcBuf索引）；如果>=0 
	tokEnd int          // 标记文本尾部（srcBuf索引）

	// 一个字符前瞻
	ch rune // 当前srcPos前的字符

	// 对于遇到的每个错误调用错误，则有效。如果未设置错误
	// 函数，则会将错误报告给操作系统。斯特德尔。
	Error func(s *Scanner, msg string)

	// ErrorCount对于遇到的每个错误都会增加一。
	ErrorCount int

	// 模式字段控制识别哪些令牌。例如，
	// 要识别整数，请在模式中设置ScanInts位。该字段可以是
	// 随时更改。
	Mode uint

	// 空白字段控制哪些字符被识别为空白。要将字符ch<=''识别为空白，
	// 将第ch位设置为空白（对于值ch>''，扫描仪的行为未定义）。该字段可以随时更改。
	Whitespace uint64

	// IsIdentRune是一个谓词，控制接受的字符
	// 作为标识符中的第i个符文。有效字符集
	// 不能与空白字符集相交。
	// 如果未设置IsIdentRune函数，则常规Go标识符将被
	// 接受。该字段可以随时更改。
	IsIdentRune func(ch rune, i int) bool

	// 最近扫描的令牌的起始位置；通过扫描设置。
	// 调用Init或Next将使位置无效（Line==0）。
	// 扫描器始终保持文件名字段不变。
	// 如果报告错误（通过错误）且位置无效，则
	// 扫描仪不在令牌内。在这种情况下，调用Pos获取错误
	// 位置，或在最近扫描的令牌之后立即获取位置
	// 位置。
	Position
}

// Init使用新源初始化扫描仪并返回s。
// Error设置为nil，ErrorCount设置为0，Mode设置为GoTokens，
// Whitespace设置为GoWhitespace。
func (s *Scanner) Init(src io.Reader) *Scanner {
	s.src = src

	// 初始化源缓冲区
	// （对next（）的第一次调用将通过调用src来填充它。读取）
	s.srcBuf[0] = utf8.RuneSelf // sentinel 
	s.srcPos = 0
	s.srcEnd = 0

	// 初始化源位置
	s.srcBufOffset = 0
	s.line = 1
	s.column = 0
	s.lastLineLen = 0
	s.lastCharLen = 0

	// 初始化令牌文本缓冲区
	// （第一次调用next（）时需要）。
	s.tokPos = -1

	// 初始化一个字符前瞻
	s.ch = -2 // 尚未读取字符，未读取EOF 

	// 初始化公共字段
	s.Error = nil
	s.ErrorCount = 0
	s.Mode = GoTokens
	s.Whitespace = GoWhitespace
	s.Line = 0 // 使令牌位置无效

	return s
}

// 下一次读取并返回下一个Unicode字符。它的设计是这样的：在常见的ASCII 
// 情况下，只需做少量的工作（一个测试用于检查ASCII和缓冲区结束，另一个测试
// 用于检查换行符）。
func (s *Scanner) next() rune {
	ch, width := rune(s.srcBuf[s.srcPos]), 1

	if ch >= utf8.RuneSelf {
		// 不常见的情况：没有ASCII或没有足够的字节
		for s.srcPos+utf8.UTFMax > s.srcEnd && !utf8.FullRune(s.srcBuf[s.srcPos:s.srcEnd]) {
			// 没有足够的字节：读取更多，但首先，如果任何
			if s.tokPos >= 0 {
				s.tokBuf.Write(s.srcBuf[s.tokPos:s.srcPos])
				s.tokPos = 0
				// s.tokEnd是通过Scan（）
			}
			// 将未读字节移到缓冲区的开头
			copy(s.srcBuf[0:], s.srcBuf[s.srcPos:s.srcEnd])
			s.srcBufOffset += s.srcPos
			// 读取更多字节
			// （io.Reader在到达
			// 读取内容结束时必须返回io.EOF——仅返回
			// n==0将使此循环永远重试；但在这种情况下，
			// 错误在读卡器实现中）
			i := s.srcEnd - s.srcPos
			n, err := s.src.Read(s.srcBuf[i:bufLen])
			s.srcPos = 0
			s.srcEnd = i + n
			s.srcBuf[s.srcEnd] = utf8.RuneSelf // 哨兵
			if err != nil {
				if err != io.EOF {
					s.error(err.Error())
				}
				if s.srcEnd == 0 {
					if s.lastCharLen > 0 {
						// 前面的字符不是EOF 
						s.column++
					}
					s.lastCharLen = 0
					return EOF
				}
				// 如果err==EOF，我们不会得到更多的
				// 字节；中断以避免无限循环。如果
				// err是另外一回事，我们不知道
				// 我们是否可以得到更多字节；这样也就断了。
				break
			}
		}
		// 至少一个字节
		ch = rune(s.srcBuf[s.srcPos])
		if ch >= utf8.RuneSelf {
			// 不常见的情况：非ASCII 
			ch, width = utf8.DecodeRune(s.srcBuf[s.srcPos:s.srcEnd])
			if ch == utf8.RuneError && width == 1 {
				// 纠正错误位置的前进
				s.srcPos += width
				s.lastCharLen = width
				s.column++
				s.error("invalid UTF-8 encoding")
				return ch
			}
		}
	}

	// 前进
	s.srcPos += width
	s.lastCharLen = width
	s.column++

	// 特殊情况
	switch ch {
	case 0:
		// 与其他工具的兼容性
		s.error("invalid character NUL")
	case '\n':
		s.line++
		s.lastLineLen = s.column
		s.column = 0
	}

	return ch
}

// 下一步读取并返回下一个Unicode字符。
// 在源代码末尾返回EOF。它通过调用s.error（如果不是nil）报告
// 读取错误；否则
// 它会向操作系统打印一条错误消息。斯特德尔。下一步不更新扫描仪的位置字段；使用Pos（）来
// 获取当前位置。
func (s *Scanner) Next() rune {
	s.tokPos = -1 // 不收集令牌文本
	s.Line = 0    // 使令牌位置无效
	ch := s.Peek()
	if ch != EOF {
		s.ch = s.next()
	}
	return ch
}

// Peek返回源中的下一个Unicode字符，而不推进
// 扫描仪。如果扫描仪的位置是源的最后一个
// 字符，则返回EOF。
func (s *Scanner) Peek() rune {
	if s.ch == -2 {
		// 此代码仅针对第一个字符
		s.ch = s.next()
		if s.ch == '\uFEFF' {
			s.ch = s.next() // 忽略BOM 
		}
	}
	return s.ch
}

func (s *Scanner) error(msg string) {
	s.tokEnd = s.srcPos - s.lastCharLen // 确保令牌文本终止
	s.ErrorCount++
	if s.Error != nil {
		s.Error(s, msg)
		return
	}
	pos := s.Position
	if !pos.IsValid() {
		pos = s.Pos()
	}
	fmt.Fprintf(os.Stderr, "%s: %s\n", pos, msg)
}

func (s *Scanner) errorf(format string, args ...any) {
	s.error(fmt.Sprintf(format, args...))
}

func (s *Scanner) isIdentRune(ch rune, i int) bool {
	if s.IsIdentRune != nil {
		return s.IsIdentRune(ch, i)
	}
	return ch == '_' || unicode.IsLetter(ch) || unicode.IsDigit(ch) && i > 0
}

func (s *Scanner) scanIdentifier() rune {
	// 我们知道第0个符文是正确的；从下一个开始扫描
	ch := s.next()
	for i := 1; s.isIdentRune(ch, i); i++ {
		ch = s.next()
	}
	return ch
}

func lower(ch rune) rune     { return ('a' - 'A') | ch } // 返回小写字母ch，如果ch是ASCII字母
func isDecimal(ch rune) bool { return '0' <= ch && ch <= '9' }
func isHex(ch rune) bool     { return '0' <= ch && ch <= '9' || 'a' <= lower(ch) && lower(ch) <= 'f' }

// 数字接受从ch0开始的序列{digit |''}。
// 如果基数<=10，则数字接受任何十进制数字，但记录
// 第一个无效数字>=base in*invalid If*invalid==0。
// digits返回序列
// 中不再包含的第一个符文，以及一个描述序列中是否包含
// 位（设置了位0）或分隔符“”（设置了位1）的位集。
func (s *Scanner) digits(ch0 rune, base int, invalid *rune) (ch rune, digsep int) {
	ch = ch0
	if base <= 10 {
		max := rune('0' + base)
		for isDecimal(ch) || ch == '_' {
			ds := 1
			if ch == '_' {
				ds = 2
			} else if ch >= max && *invalid == 0 {
				*invalid = ch
			}
			digsep |= ds
			ch = s.next()
		}
	} else {
		for isHex(ch) || ch == '_' {
			ds := 1
			if ch == '_' {
				ds = 2
			}
			digsep |= ds
			ch = s.next()
		}
	}
	return
}

func (s *Scanner) scanNumber(ch rune, seenDot bool) (rune, rune) {
	base := 10         // 数字基
	prefix := rune(0)  // 0（十进制）、'0'（0-八进制）、'x'、'o'或'b'中的一个
	digsep := 0        // 位0：数字存在，位1：'.'存在
	invalid := rune(0) // 文字无效数字，或0 

	// 整数部分
	var tok rune
	var ds int
	if !seenDot {
		tok = Int
		if ch == '0' {
			ch = s.next()
			switch lower(ch) {
			case 'x':
				ch = s.next()
				base, prefix = 16, 'x'
			case 'o':
				ch = s.next()
				base, prefix = 8, 'o'
			case 'b':
				ch = s.next()
				base, prefix = 2, 'b'
			default:
				base, prefix = 8, '0'
				digsep = 1 // 前导0 
			}
		}
		ch, ds = s.digits(ch, base, &invalid)
		digsep |= ds
		if ch == '.' && s.Mode&ScanFloats != 0 {
			ch = s.next()
			seenDot = true
		}
	}

	// 小数部分
	if seenDot {
		tok = Float
		if prefix == 'o' || prefix == 'b' {
			s.error("invalid radix point in " + litname(prefix))
		}
		ch, ds = s.digits(ch, base, &invalid)
		digsep |= ds
	}

	if digsep&1 == 0 {
		s.error(litname(prefix) + " has no digits")
	}

	// 指数
	if e := lower(ch); (e == 'e' || e == 'p') && s.Mode&ScanFloats != 0 {
		switch {
		case e == 'e' && prefix != 0 && prefix != '0':
			s.errorf("%q exponent requires decimal mantissa", ch)
		case e == 'p' && prefix != 'x':
			s.errorf("%q exponent requires hexadecimal mantissa", ch)
		}
		ch = s.next()
		tok = Float
		if ch == '+' || ch == '-' {
			ch = s.next()
		}
		ch, ds = s.digits(ch, 10, nil)
		digsep |= ds
		if ds&1 == 0 {
			s.error("exponent has no digits")
		}
	} else if prefix == 'x' && tok == Float {
		s.error("hexadecimal mantissa requires a 'p' exponent")
	}

	if tok == Int && invalid != 0 {
		s.errorf("invalid digit %q in %s", invalid, litname(prefix))
	}

	if digsep&2 != 0 {
		s.tokEnd = s.srcPos - s.lastCharLen // 确保标记文本已终止
		if i := invalidSep(s.TokenText()); i >= 0 {
			s.error("'_' must separate successive digits")
		}
	}

	return tok, ch
}

func litname(prefix rune) string {
	switch prefix {
	default:
		return "decimal literal"
	case 'x':
		return "hexadecimal literal"
	case 'o', '0':
		return "octal literal"
	case 'b':
		return "binary literal"
	}
}

// invalidSep返回x中第一个无效分隔符的索引，或-1。
func invalidSep(x string) int {
	x1 := ' ' // 前缀char，我们只关心它是否是'x'
	d := '.'  // 数字，是“u”、“0”（一个数字）或“.”中的一个（其他任何内容）前缀算作数字
	i := 0

	if len(x) >= 2 && x[0] == '0' {
		x1 = lower(rune(x[1]))
		if x1 == 'x' || x1 == 'o' || x1 == 'b' {
			d = '0'
			i = 2
		}
	}

	// 尾数和指数
	for ; i < len(x); i++ {
		p := d // 前一个数字
		d = rune(x[i])
		switch {
		case d == '_':
			if p != '0' {
				return i
			}
		case isDecimal(d) || x1 == 'x' && isHex(d):
			d = '0'
		default:
			if p == '_' {
				return i - 1
			}
			d = '.'
		}
	}
	if d == '_' {
		return len(x) - 1
	}

	return -1
}

func digitVal(ch rune) int {
	switch {
	case '0' <= ch && ch <= '9':
		return int(ch - '0')
	case 'a' <= lower(ch) && lower(ch) <= 'f':
		return int(lower(ch) - 'a' + 10)
	}
	return 16 // 比任何法定数字都大val 
}

func (s *Scanner) scanDigits(ch rune, base, n int) rune {
	for n > 0 && digitVal(ch) < base {
		ch = s.next()
		n--
	}
	if n > 0 {
		s.error("invalid char escape")
	}
	return ch
}

func (s *Scanner) scanEscape(quote rune) rune {
	ch := s.next() // 读取“/”后的字符
	switch ch {
	case 'a', 'b', 'f', 'n', 'r', 't', 'v', '\\', quote:
		// 无需执行
		ch = s.next()
	case '0', '1', '2', '3', '4', '5', '6', '7':
		ch = s.scanDigits(ch, 8, 3)
	case 'x':
		ch = s.scanDigits(s.next(), 16, 2)
	case 'u':
		ch = s.scanDigits(s.next(), 16, 4)
	case 'U':
		ch = s.scanDigits(s.next(), 16, 8)
	default:
		s.error("invalid char escape")
	}
	return ch
}

func (s *Scanner) scanString(quote rune) (n int) {
	ch := s.next() // 读取引用后的字符
	for ch != quote {
		if ch == '\n' || ch < 0 {
			s.error("literal not terminated")
			return
		}
		if ch == '\\' {
			ch = s.scanEscape(quote)
		} else {
			ch = s.next()
		}
		n++
	}
	return
}

func (s *Scanner) scanRawString() {
	ch := s.next() // 读取“`”后的字符
	for ch != '`' {
		if ch < 0 {
			s.error("literal not terminated")
			return
		}
		ch = s.next()
	}
}

func (s *Scanner) scanChar() {
	if s.scanString('\'') != 1 {
		s.error("invalid char literal")
	}
}

func (s *Scanner) scanComment(ch rune) rune {
	// ch='/'| | ch='*'
	if ch == '/' {
		// 行注释
		ch = s.next() // 读取“
		for ch != '\n' && ch >= 0 {
			ch = s.next()
		}
		return ch
	}

	// 一般注释
	ch = s.next() // 读取“/*”
	for {
		if ch < 0 {
			s.error("comment not terminated")
			break
		}
		ch0 := ch
		ch = s.next()
		if ch0 == '*' && ch == '/' {
			ch = s.next()
			break
		}
	}
	return ch
}

// 扫描从源读取下一个令牌或Unicode字符并返回。
// 它只识别具有相应模式位（1<-t）的令牌t一切就绪。
// 在源代码末尾返回EOF。它通过调用s.Error（如果不是nil）报告扫描仪错误（读取和
// 令牌错误）；否则，它会向操作系统打印一条错误
// 消息。斯特德尔。
func (s *Scanner) Scan() rune {
	ch := s.Peek()

	// 重置令牌文本位置
	s.tokPos = -1
	s.Line = 0

redo:
	// 跳过空白
	for s.Whitespace&(1<<uint(ch)) != 0 {
		ch = s.next()
	}

	// 开始收集令牌文本
	s.tokBuf.Reset()
	s.tokPos = s.srcPos - s.lastCharLen

	// 设置令牌位置
	// （这是Pos（）中代码的略微优化版本）
	s.Offset = s.srcBufOffset + s.tokPos
	if s.column > 0 {
		// 常见情况：最后一个字符不是“\n”
		s.Line = s.line
		s.Column = s.column
	} else {
		// 最后一个字符是“\n”
		// （我们不能在源代码的开头，因为我们至少调用了一次next（））
		s.Line = s.line - 1
		s.Column = s.lastLineLen
	}

	// 确定标记值
	tok := ch
	switch {
	case s.isIdentRune(ch, 0):
		if s.Mode&ScanIdents != 0 {
			tok = Ident
			ch = s.scanIdentifier()
		} else {
			ch = s.next()
		}
	case isDecimal(ch):
		if s.Mode&(ScanInts|ScanFloats) != 0 {
			tok, ch = s.scanNumber(ch, false)
		} else {
			ch = s.next()
		}
	default:
		switch ch {
		case EOF:
			break
		case '"':
			if s.Mode&ScanStrings != 0 {
				s.scanString('"')
				tok = String
			}
			ch = s.next()
		case '\'':
			if s.Mode&ScanChars != 0 {
				s.scanChar()
				tok = Char
			}
			ch = s.next()
		case '.':
			ch = s.next()
			if isDecimal(ch) && s.Mode&ScanFloats != 0 {
				tok, ch = s.scanNumber(ch, true)
			}
		case '/':
			ch = s.next()
			if (ch == '/' || ch == '*') && s.Mode&ScanComments != 0 {
				if s.Mode&SkipComments != 0 {
					s.tokPos = -1 // 不收集标记文本
					ch = s.scanComment(ch)
					goto redo
				}
				ch = s.scanComment(ch)
				tok = Comment
			}
		case '`':
			if s.Mode&ScanRawStrings != 0 {
				s.scanRawString()
				tok = RawString
			}
			ch = s.next()
		default:
			ch = s.next()
		}
	}

	// 结束标记文本
	s.tokEnd = s.srcPos - s.lastCharLen

	s.ch = ch
	return tok
}

// Pos返回字符在
// 最后一次调用next返回的字符或标记的位置或者扫描。
// 使用扫描仪的位置字段作为最近扫描的令牌的开始位置。
func (s *Scanner) Pos() (pos Position) {
	pos.Filename = s.Filename
	pos.Offset = s.srcBufOffset + s.srcPos - s.lastCharLen
	switch {
	case s.column > 0:
		// 常见情况：最后一个字符不是'\n'
		pos.Line = s.line
		pos.Column = s.column
	case s.lastLineLen > 0:
		// 最后一个字符是'\n'
		pos.Line = s.line - 1
		pos.Column = s.lastLineLen
	default:
		// 在源代码的开头
		pos.Line = 1
		pos.Column = 1
	}
	return
}

// TokenText返回与最近扫描的令牌对应的字符串。
// 在调用Scan和在调用Scanner后有效。错误
func (s *Scanner) TokenText() string {
	if s.tokPos < 0 {
		// 无标记文本
		return ""
	}

	if s.tokEnd < s.tokPos {
		// 如果达到EOF，s.tokEnd设置为-1（s.srcPos==0）
		s.tokEnd = s.tokPos
	}
	// s.tokEnd>=s.tokPos 

	if s.tokBuf.Len() == 0 {
		// 常见情况：整个令牌文本仍在srcBuf 
		return string(s.srcBuf[s.tokPos:s.tokEnd])
	}

	// 部分令牌文本保存在tokBuf中：将其余部分也保存在
	// tokBuf并返回其内容
	s.tokBuf.Write(s.srcBuf[s.tokPos:s.tokEnd])
	s.tokPos = s.tokEnd // 确保令牌文本的幂等性（）调用
	return s.tokBuf.String()
}
